package lp.polimi.treefinder;

import java.io.IOException;
import java.util.List;
import java.util.Locale;



import android.app.Activity;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.Fragment;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesClient;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.location.LocationClient;

/**
 * Invisible fragment using the Google Location API to perform in background the address search.
 * First latitude and longitude coordinates are retrieved, exploiting the currently active device modules
 * (Wi-Fi, Mobile data, GPS), then a request to the Geocoder (asyc task GetAddressTask) is sent
 * and finally the address is formatted and returned
 */
public class LocalizationFragment extends Fragment implements
	GooglePlayServicesClient.ConnectionCallbacks,
	GooglePlayServicesClient.OnConnectionFailedListener {


	private final static int CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000;
	private LocationClient mLocationClient;
	private LocalizationFragmentListener listener;
	
	/**
	 * Finds the current location and starts the GetAddressTask.
	 * At the end of the procedure (no matter what if successful or not), onAddressRetrieved() is called
	 * and the location (or null) is returned to the caller.
	 */
	public void getCurrentLocation () {
		Location mLocation = mLocationClient.getLastLocation();
		if (mLocation == null)
			listener.onAddressRetrieved(null);
		else if (Geocoder.isPresent())
			new GetAddressTask(getActivity()).execute(mLocation);
		else
			listener.onAddressRetrieved(null);
	}

	
	@Override
	public void onAttach(Activity activity) {
		super.onAttach(activity);
		try {
			listener = (LocalizationFragmentListener) activity;
		} catch (ClassCastException e) {
			throw new ClassCastException(activity + " must implement OnAddressRetrieved method");
		}
	}

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		if (servicesConnected())
			mLocationClient = new LocationClient(getActivity(), this, this);
	}

	@Override
	public void onStart() {
		super.onStart();
		mLocationClient.connect();
	}

	@Override
	public void onStop() {
		mLocationClient.disconnect();
		super.onStop();
	}



	// Define a DialogFragment that displays the error dialog
	public static class ErrorDialogFragment extends DialogFragment {

		private Dialog mDialog;

		public ErrorDialogFragment() {
			super();
			mDialog = null;
		}
		public void setDialog(Dialog dialog) {
			mDialog = dialog;
		}
		@Override
		public Dialog onCreateDialog(Bundle savedInstanceState) {
			return mDialog;
		}
	}

	/*
	 * Handle results returned by Google Play services
	 */
	@Override
	public void onActivityResult(int requestCode, int resultCode, Intent data) {
		switch (requestCode) {
		case CONNECTION_FAILURE_RESOLUTION_REQUEST :
			/*
			 * If the result code is Activity.RESULT_OK, try
			 * to connect again
			 */
			if (resultCode == Activity.RESULT_OK)
				/*
				 * Try the request again
				 */
				break;
		}
	}


	private boolean servicesConnected() {
		// Check that Google Play services is available
		int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(getActivity());
		if (!(ConnectionResult.SUCCESS == resultCode)) {
			Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(resultCode, getActivity(), CONNECTION_FAILURE_RESOLUTION_REQUEST);
			// If Google Play services can provide an error dialog
			if (errorDialog != null) {
				// Create a new DialogFragment for the error dialog
				ErrorDialogFragment errorFragment =
						new ErrorDialogFragment();
				// Set the dialog in the DialogFragment
				errorFragment.setDialog(errorDialog);
				// Show the error dialog in the DialogFragment
				errorFragment.show(getFragmentManager(),
						getResources().getString(R.string.error_location));
			}
			return false;
		}
		return true;
	}

	@Override
	public void onConnected(Bundle dataBundle) {
		Log.d("Location", "Connected!");
	}

	@Override
	public void onDisconnected() {
		Log.d("Location", "Disconnected!");
	}


	/*
	 * Called by Location Services if the attempt to
	 * Location Services fails.
	 */
	@Override
	public void onConnectionFailed(ConnectionResult connectionResult) {
		/*
		 * Google Play services can resolve some errors it detects.
		 * If the error has a resolution, try sending an Intent to
		 * start a Google Play services activity that can resolve
		 * error.
		 */
		if (connectionResult.hasResolution()) {
			try {
				// Start an Activity that tries to resolve the error
				connectionResult.startResolutionForResult(getActivity(), CONNECTION_FAILURE_RESOLUTION_REQUEST);
				/*
				 * Thrown if Google Play services canceled the original
				 * PendingIntent
				 */
			} catch (IntentSender.SendIntentException e) {
				// Log the error
				e.printStackTrace();
			}
		} else {
			//TODO stop tagging thread and notify error.
		}
	}



	private class GetAddressTask extends AsyncTask<Location, Void, String[]> {
		Context mContext;
		public GetAddressTask(Context context) {
			super();
			mContext = context;
		}
		/*
		 * Get a Geocoder instance, get the latitude and longitude
		 * look up the address, and return it
		 *
		 * @params params One or more Location objects
		 * @return A string containing the address of the current
		 * location, or an empty string if no address can be found,
		 * or an error message
		 */
		@Override
		protected String[] doInBackground(Location... params) {
			Geocoder geocoder =
					new Geocoder(mContext, Locale.getDefault());
			// Get the current location from the input parameter list
			Location loc = params[0];
			// Create a list to contain the result address
			List<Address> addresses = null;
			try {
				/*
				 * Return 1 address.
				 */
				addresses = geocoder.getFromLocation(loc.getLatitude(), loc.getLongitude(), 1);
			} catch (IOException e1) {
				Log.e("LocationSampleActivity", "IO Exception in getFromLocation()");
				e1.printStackTrace();
				return null;
			} catch (IllegalArgumentException e2) {
				// Error message to post in the log
				String errorString = "Illegal arguments " +
						Double.toString(loc.getLatitude()) +
						" , " +
						Double.toString(loc.getLongitude()) +
						" passed to address service";
				Log.e("LocationSampleActivity", errorString);
				e2.printStackTrace();
				return null;
			}
			String[] result = new String[3];
			// If the reverse geocode returned an address
			if (addresses != null && addresses.size() > 0) {
				// Get the first address
				Address address = addresses.get(0);
				/*
				 * Format the first line of address (if available),
				 * city, and country name.
				 */
				result[0]=address.getAddressLine(0);
				result[1]=address.getLocality();
				result[2]=address.getCountryName();
				return result;
			} else {
				return null;
			}
		}

		@Override
		protected void onPostExecute(String[] result) {
			listener.onAddressRetrieved(result);
		}

	}
	
	public interface LocalizationFragmentListener {
		public void onAddressRetrieved (String[] result);
	}
}
